**********************************************************************
*                                                                    *
*             Apple IIGS Source Code Sampler, Volume I               *
*                                                                    *
*             Copyright (c) Apple Computer, Inc. 1988                *
*                       All Rights Reserved                          *
*                                                                    *
*            Written by Apple II Developer Tech Support              *
*                                                                    *
*                                                                    *
*                                                                    *
*  ----------------------------------------------------------------  *
*                                                                    *
*     This program and its derivatives are licensed only for         *
*     use on Apple computers.                                        *
*                                                                    *
*     Works based on this program must contain and                   *
*     conspicuously display this notice.                             *
*                                                                    *
*     This software is provided for your evaluation and to           *
*     assist you in developing software for the Apple IIGS           *
*     computer.                                                      *
*                                                                    *
*     DISCLAIMER OF WARRANTY                                         *
*                                                                    *
*     THE SOFTWARE IS PROVIDED "AS IS" WITHOUT                       *
*     WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED,               *
*     WITH RESPECT TO ITS MERCHANTABILITY OR ITS FITNESS             *
*     FOR ANY PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO             *
*     THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH            *
*     YOU.  SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU (AND            *
*     NOT APPLE OR AN APPLE AUTHORIZED REPRESENTATIVE)               *
*     ASSUME THE ENTIRE COST OF ALL NECESSARY SERVICING,             *
*     REPAIR OR CORRECTION.                                          *
*                                                                    *
*     Apple does not warrant that the functions                      *
*     contained in the Software will meet your requirements          *
*     or that the operation of the Software will be                  *
*     uninterrupted or error free or that defects in the             *
*     Software will be corrected.                                    *
*                                                                    *
*     SOME STATES DO NOT ALLOW THE EXCLUSION                         *
*     OF IMPLIED WARRANTIES, SO THE ABOVE EXCLUSION MAY              *
*     NOT APPLY TO YOU.  THIS WARRANTY GIVES YOU SPECIFIC            *
*     LEGAL RIGHTS AND YOU MAY ALSO HAVE OTHER RIGHTS                *
*     WHICH VARY FROM STATE TO STATE.                                *
*                                                                    *
*                                                                    *
**********************************************************************


*******************************************************************************
* Jiffy Windows - Pixel Transfer Demonstration
*
* (C)  Copyright Apple Computer, Inc. 1988
* All rights reserved.
*
* by Keith Rollin
* July 13, 1988
*
* This program shows the use of PPToPort and OffScreen GrafPorts in
* the implementation of a window content caching system. It presents 5 "Test"
* windows and a large window full of text to show the effects of this caching.
*
* Caching is setup at the time the window is created and performed the first
* time the window needs to be updated (drawn). Before the window is created,
* a routine (AddCacheRecord) is called to create a "Cache Record". The handle
* to this cache record is stored in the window's RefCon, and contains infor-
* mation on the offscreen image. When it comes time to draw to the screen, this
* record is examined to see if there is any information buffered already. If
* not, then drawing occurs to the offscreen buffer. After an offscreen image
* has been made, updates are performed by calling PPToPort to copy the image
* from the offscreen buffer to the screen. If, for any reason, an offscreen
* buffer cannot be made, drawing occurs to the window directly.
*
* An effort has been made to make this caching as transparent as possible.
* Windows act normally, and the routines that update them in a normal envi-
* ronment can be used in this buffered environment. The caching routine is
* inserted in between TaskMaster and the update routine, and calls the update
* routine almost as if it were called by TaskMaster. The only difference is
* that the routine is drawing to an offscreen GrafPort, and not a Window
* record.
*
*
* Files:            System macros and equates.
*                   Jiffy.stds.aii
*
*
* Modification History:
*
* Version 1.0       Rollin
*
*      July 13, 1988
*
*      First Release
*
*******************************************************************************

                    mcopy jiffy.macros
                    copy 2/ainclude/E16.Quickdraw
                    copy 2/ainclude/E16.Memory
                    copy 2/ainclude/E16.EVENT
                    copy 2/ainclude/E16.Window
                    copy 2/ainclude/E16.Dialog

*******************************************************************************
*
*    Equates used in this program.
*
*******************************************************************************
DPHandle            gequ 0              ; Handle to Tool Direct Page area
DPPointer           gequ DPHandle+4     ; Pointer to Tool Direct Page area
deref               gequ DPPointer+4    ; Temporary Handle dereference area
CPPtr               gequ Deref+4        ; pointer to current caching parms
Temp1               gequ CPPtr+4        ; temp pointer
Temp2               gequ Temp1+4        ; temp pointer

ScreenMode          gequ Mode640
ScreenWidth         gequ 640

CacheRecordSize     gequ 12

                    EJECT
*******************************************************************************
*
Main                start
*
* Description:      This is the main routine. It calls routines to Initialize
*                   the tools, initialize application specific data, run the
*                   main EventLoop, close the application, and close the tools.
*                   Then it calls the ProDOS Quit command.
*
*
* Inputs:           NONE
*
* Outputs:          NONE
*
* External Refs:
*                   Import InitTools
*                   Import InitApp
*                   Import EventLoop
*                   Import CloseApp
*                   Import CloseTools
*                   Import QuitParms
*
* Entry Points:     NONE
*
*******************************************************************************
                    using globals

                    jsr InitTools
                    jsr InitApp

                    _ShowCursor

                    jsr EventLoop

                    jsr CloseApp
                    jsr CloseTools

                    _Quit QuitParms

                    end

                    EJECT
*******************************************************************************
*
Globals             data
*
* Description:      Holder of all of our data.
*
*
* Inputs:           N/A
*
* Outputs:          N/A
*
* External Refs:
*                   Import DrawOdyRtn
*                   Import CacheContent
*                   Import DrawContent
*
* Entry Points:
*                   Export QuitParms    ; used by Main
*
*******************************************************************************
*
* Standard global data
*
*******************************************************************************

TitleString         str 'Jiffy Windows'
AutString           str 'By Keith Rollin, Apple II DTS'
CopyString          str '(c)1988 Apple Computer'
VerString           str 'Version 1.0           July 13, 1988'
AboutData           dc c'73/*0',i1'13',c'*1',i1'13'
                    dc c'*2',i1'13',c'*3/^#0',i1'0'
SubStrings          dc i4'TitleString,AutString,CopyString,VerString'

MenuHeight          ds 2                ; Stored height of menu bar
MyID                ds 2                ; Application ID
MyDP                ds 2                ; My direct page storage

QuitFlag            ds 2
QuitParms           dc i4'0'            ; Pathname of next app
                    dc i2'$00'          ; flags

EventRecord         ANOP
EventWhat           ds 2
EventMessage        ds 4
EventWhen           ds 4
EventWhere          ds 4
EventModifiers      ds 2
TaskData            ds 4
TaskMask            dc i4'$0000FFFF'

                    EJECT
*******************************************************************************
*
* Application specific global data
*
*******************************************************************************

; This is a list of pointers to the text that is used to create our menus. It
; is used by InitApp to find all of the menu templates and use them to create
; our menubar. This loop loads MenuPtrLen-4 into an index, gets the
; corresponding menu template pointer in this table, and uses that in a
; NewMenu call. It then decrements the index by 4, and repeats the procees
; until the index is negative.

MenuPtr             dc i4'AppMenu'
                    dc i4'FileMenu'
                    dc i4'EditMenu'
MenuPtrLen          equ *-MenuPtr


; Menu list: menu items should be numbered consecutivly starting from 250.
; As a convention, use 256 as about and 257 as Quit.

AppMenu             dc c'$$@\N1',i1'$0D'
                    dc c'--About Jiffy Windows...\N256V',i1'0'
                    dc c'.'
FileMenu            dc c'$$  File  \N2',i1'0'
                    dc c'--Open Test Windows\N258',i1'0'
                    dc c'--Read a book\N259',i1'0'
                    dc c'--Close\N255DV',i1'0'
                    dc c'--Quit\N257*Qq',i1'0'
                    dc c'.'
EditMenu            dc c'$$  Edit  \N3',i1'0'
                    dc c'--Undo\N250*ZzVD',i1'0'
                    dc c'--Cut\N251*XxD',i1'0'
                    dc c'--Copy\N252*CcD',i1'0'
                    dc c'--Paste\N253*VvD',i1'0'
                    dc c'--Clear\N254D',i1'0'
                    dc c'.'

wPtr1               dc i4'0'
wPtr2               dc i4'0'
wPtr3               dc i4'0'
wPtr4               dc i4'0'
wPtr5               dc i4'0'
OdyWindow           dc i4'0'

WindowTitle1        str '  Window 1 (not Cached)  '
WindowTitle2        str '  Window 2 (not Cached)  '
WindowTitle3        str '  Window 3 (Cached)  '
WindowTitle4        str '  Window 4 (Cached)  '
WindowTitle5        str '  Window 5 (Cached)  '

wtPtrs              dc i4'WindowTitle1,WindowTitle2,WindowTitle3'
                    dc i4'WindowTitle4,WindowTitle5'

Stripes             ANOP
                    dc i2'$0000'        ; Black frame
                    dc i2'$0f00'        ; Inact TBcolor, Inact TColor, Tcolor
                    dc i2'$020f'        ; Pattern, Pattern color, BG color
                    dc i2'$0000'        ; Frame growbox unselcted/selected
                    dc i2'$00f0'        ; infobar color (white)

MWindData           dc i2'MWEnd-MWindData'
                    dc i2'fTitle+fClose+fMove'
WindowTitle         dc i4'0'            ; Ptr to title
                    dc i4'0'            ; RefCon
                    dc i2'0,0,0,0'      ; Full Size (0= default)
                    dc i4'stripes'      ; Color Table Pointer
                    dc i2'0,0'          ; Vertical/Horizontal origin
                    dc i2'0,0'          ; Data area height, width
                    dc i2'0,0'          ; Max Cont height, width
                    dc i2'0,0'          ; Pixels to scroll vert'ly, horiz'ly
                    dc i2'0,0'          ; Pixels to page vert'ly, horiz'ly
                    dc i4'0'            ; Information bar refcon.
                    dc i2'0'            ; Info bar height
                    dc i4'0'            ; DefProc.
                    dc i4'0'            ; Routine to draw info. bar.
contDraw            dc i4'CacheContent' ; Routine to cache/draw content.
wPosition           dc i2'10,40,120,400'                    ; Size and position
                    dc i4'$FFFFFFFF'    ; Plane to put window in (bottommost)
                    dc i4'0'            ; Address for window rec (0 to alloc)
MWEnd               ANOP
;
; These parms MUST follow immediately after the window record
;
MWCacheParms        dc i4'DrawContent-1' ; real draw routine
                    dc i4'0'            ; space for caching handle
                    dc i2'110,360'      ; Height, width


OdyTitle            str '  Odyssey  '
OdyWindData         dc i2'OdyEnd-OdyWindData'
                    dc i2'fTitle+fClose+fMove+fRScroll+fBScroll+fGrow'
                    dc i4'OdyTitle'     ; Ptr to title
                    dc i4'0'            ; RefCon
                    dc i2'0,0,0,0'      ; Full Size (0= default)
                    dc i4'stripes'      ; Color Table Pointer
                    dc i2'0,0'          ; Vertical/Horizontal origin
                    dc i2'797,600'      ; Data area height, width
                    dc i2'0,0'          ; Max Cont height, width
                    dc i2'32,32'        ; Pixels to scroll vert'ly, horiz'ly
                    dc i2'0,0'          ; Pixels to page vert'ly, horiz'ly
                    dc i4'0'            ; Information bar refcon.
                    dc i2'0'            ; Info bar height
                    dc i4'0'            ; DefProc.
                    dc i4'0'            ; Routine to draw info. bar.
                    dc i4'CacheContent' ; Routine to cache/draw content.
                    dc i2'30,5,185,605' ; Size and position
                    dc i4'$FFFFFFFF'    ; Plane to put window in (bottommost).
                    dc i4'0'            ; Address for window rec (0 to alloc)
OdyEnd              ANOP
;
; These parms MUST follow immediately after the window record
;
OdyCacheParms       dc i4'DrawOdyRtn-1'
                    dc i4'0'            ; space for caching handle
                    dc i2'792,640'      ; Height, width

Odyssey             ANOP
                    dc i1'13,13'
                    dc i1'1',c'J',i1'1,0'
                    dc i1'1',c'F',i1'0,0,0,16'              ; Shaston 16
                    dc c'1',i1'13'
                    dc c'Boot Camp',i1'13'
                    dc i1'1',c'J',i1'2,0'
                    dc i1'1',c'F',i1'0,0,0,8'               ; Shaston 8
                    dc i1'13,13'
                    dc c'      At Pepsi, we lived for the smallest details.'
                    dc i1'13,13'
                    dc c'     The moment I entered the boardroom, I knew the '
                    dc c'meeting would be momentous. For years, these '
                    dc c'executive sessions had followed a carefully '
                    dc c'orchestrated ritual. Today, there was a subtle, '
                    dc c'though telling, difference.',i1'13,13'

                    dc c'     Ordinarily, the conference table''s 21-foot-'
                    dc c'long surface, polished to look more like glass than '
                    dc c'the rare burl of Carpathian elm, would be touched '
                    dc c'only by PepsiCo board members, and then no more '
                    dc c'than twelve times a year. Today, the fitted leather '
                    dc c'covering that hid the gleaming top was removed.'
                    dc i1'13,13'

                    dc c'     I had been in this room at least a hundred '
                    dc c'times. I had sat worried at this table and I had '
                    dc c'nearly soared at it. I had witnessed tense '
                    dc c'colleagues - friends - being publicly hung for '
                    dc c'questions of performance, and I had seen them '
                    dc c'applauded, just as I had been here, for marketing '
                    dc c'ideas and strategies.',i1'13,13'

                    dc c'     But this meeting was different. the table''s '
                    dc c'exposed top said so. It made the spacious, '
                    dc c'windowless room all the more imposing. Like nearly '
                    dc c'everything else at PepsiCo, Inc., the room''s '
                    dc c'stately power and elegance made you stand a little '
                    dc c'straighter. A large abstact painting by Jackson '
                    dc c'Pollock''s widow, Lee Krasner, dominated the far '
                    dc c'wall. Custom-designed carpeting in earth-tone '
                    dc c'colors cushioned the floor. The bronze-plated '
                    dc c'ceiling, perhaps more appropriate for a church, '
                    dc c'reflected burnished mahogany-paneled walls. High-'
                    dc c'backed beige leather chairs, so imposing they could '
                    dc c'have carried corporate titles of their own, '
                    dc c'surrounded the boardroom table.',i1'13,13'

                    dc c'     Every two months the marketing gurus of Pepsi '
                    dc c'-- the company''s heart and soul -- would gather '
                    dc c'here, on 4/3, the executive floor of PepsiCo''s '
                    dc c'corporate headquarters in the New York City suburb '
                    dc c'of Purchase, for a private ritual in the public war '
                    dc c'on the colas between Pepsi and Coke. A. C. Neilsen '
                    dc c'Co., the country''s most prestigious market-'
                    dc c'research firm, would formally present what became '
                    dc c'popularly known as the Nielsens -- closely guarded '
                    dc c'market-share figures that showed how we were faring '
                    dc c'in our competitive battle with Coca-Cola Co. The '
                    dc c'Nielsens defined the ground rules of competition '
                    dc c'for everyone at Pepsi. They were at the epicenter '
                    dc c'of all we did. They were the non-public body counts '
                    dc c'of the Cola Wars.',i1'13,13'

                    dc c'     Like the other meetings, this one was a '
                    dc c'ceremonial event. We marked it on our calendars '
                    dc c'many weeks in advance. Everyone wore the unofficial '
                    dc c'corporate uniform: a blue pin-striped suit, white '
                    dc c'shirt, and a sincere red tie. None of us would ever '
                    dc c'remove his jacket. We dressed and acted as if we '
                    dc c'were at a meeting of the board of directors.'
                    dc i1'13,13'

                    dc c'     The Nielsen executives were the first to '
                    dc c'arrive. They carried themselves like accountants -- '
                    dc c'good presenters with bland personalities in blue '
                    dc c'and gray suits. They were followed by Pepsi''s '
                    dc c'junior executives, the support staff of market '
                    dc c'researchers and brand managers who could provide a '
                    dc c'stray fact of figure for the division officers and '
                    dc c'vice presidents who filed into the room next.'
                    dc i1'13,13'

                    dc c'     Everyone in this room was on the fast track, '
                    dc c'which at Pepsi was more like a racecourse.The '
                    dc c'senior officers here were the best of the best. In '
                    dc c'a corporation populated with bright, ambitious '
                    dc c'achievers, they were peerless. They had proven they '
                    dc c'were intellectually and physically fit supervisors '
                    dc c'in America''s corporate Marine Corps.',i1'13,13'

                    dc c'     As president of Pepsi-Cola, I entered the room '
                    dc c'next. Corporate formality dictated where all of us '
                    dc c'sat. The company''s top officers gravitated to the '
                    dc c'front of the table, the junior execs toward the '
                    dc c'back. Staff people found chairs away from the table '
                    dc c'along the walls. When everyone was in place, I went '
                    dc c'across the hall to alert PepsiCo''s president, '
                    dc c'Andrall E. Pearson, who then joined us.',i1'13,13'
                    
                    dc c'     Everyone chatted away until the heavy wooden '
                    dc c'doors opened once again. The room fell quiet as a '
                    dc c'big-boned man with a frizzy shock of white hair '
                    dc c'strode in. PepsiCo chairman and chief executive '
                    dc c'Donald M. Kendall, fifty-seven, was no taller than '
                    dc c'six feet one, but he was a man of enormous '
                    dc c'presence, respected and feared by colleagues. '
                    dc c'Today, Kendall was feeling great pleasure. He '
                    dc c'worked the room like a politician, gripping hands, '
                    dc c'slapping backs, and giving an occasional bear hug '
                    dc c'-- a patented gesture of strength and affection '
                    dc c'that Kendall reserved for only a few.'
                    dc i1'13,13,13,13'
                    dc c'From Odyssey by John Sculley',i1'13'
                    dc c'Used by permission, Harper & Row, Publishers, Inc.'
                    dc i1'13'

odysseyEnd          ANOP
                    end

                    EJECT
*******************************************************************************
*
InitApp             start
*
* Description:      Perform any application specific initialization.
*
* Inputs:           NONE
*
* Outputs:          NONE
*
* External Refs:
*                   Import SlowDraw
*
* Entry Points:     NONE
*
*******************************************************************************
                    using Globals

                    stz QuitFlag

;
; Create 5 test windows. Loop from 4 to 0, calling AddWindow with
; the loop index in the accumulator. AddWindow will use this index
; to name the window, determine if it should be cached or not, and
; stagger its position.
;

                    lda #4
                    sta Windex
loop                lda Windex
                    jsr AddWindow
                    dec Windex
                    bpl loop


;
; Create the window to hold John "The Dude" Sculley's book.
;
                    lda #odyWindData    ; create a cache record for it
                    ldx #^OdyWindData
                    jsr AddCacheRecord

                    pha
                    pha
                    PushLong #OdyWindData
                    _NewWindow
                    PullLong OdyWindow

                    rts

Windex              ds 2

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Enter this routine with an index from 0 to 4. This index is
; used to set the window's title from a list of titles, stagger
; its position and to determine whether or not it should be cached.
;

AddWindow           ANOP

;
; Turn the window counter into an array index. This index is
; stored in X and is used throughout the routine, so I need
; to be sure not to destroy it!
;
                    asl a
                    asl a
                    tax
                    stx IndexSave
;
; Set up the pointer to the window's name
;
                    lda wtPtrs,x
                    sta WindowTitle
                    lda wtPtrs+2,x
                    sta WindowTitle+2

; Bump the window's position so as to stagger them

                    ldy #6
loop2               lda wPosition,y
                    clc
                    adc #20
                    sta wPosition,y
                    dey
                    dey
                    bpl loop2

; the last 2 windows are special...they don't get cached...

                    cpx #4
                    bne CreateIt
                    lda #SlowDraw
                    sta ContDraw
                    lda #^SlowDraw
                    sta ContDraw+2

CreateIt            ANOP
                    lda #MWindData      ; create a cache record for it
                    ldx #^MWindData
                    jsr AddCacheRecord

                    pha
                    pha
                    PushLong #MWindData
                    _NewWindow

                    ldx IndexSave
                    lda 1,s
                    sta wPtr1,x         ; now save our window pointer in our
                    lda 3,s             ; window pointer array.
                    sta wPtr1+2,x
;
; the window pointer is still on the stack, so use it to
; set the current port (NewWindow doesn't do that!!!)
;
                    _SetPort

done                ANOP
                    rts

IndexSave           ds 2

;
; Just before creating a new window, we must create a cache record for it.
; This record will hold: a) the routine to draw the contents the first time,
; b) a pointer to the offscreen GrafPort, and c) the dimensions needed to
; create the GrafPort. This cache record is created from a template stored
; IMMEDIATELY after the window parameter list (after owStorage).
;
; Enter this routine with the pointer to the window parameter list in A,X.
;
AddCacheRecord      ANOP
                    sta temp1
                    stx temp1+2         ; temp1 now points to the parmlist

                    clc                 ; make a pointer to the template
                    adc #owStorage+4
                    sta temp2
                    stx temp2+2

                    pha                 ; space for the new handle
                    pha
                    PushLong #CacheRecordSize
                    PushWord MyID
                    PushWord #attrNoCross
                    PushLong #$0000
                    _NewHandle
                    PullLong myHandle   ; handle to cache record in deref

                    PushLong Temp2      ; copy the template over
                    PushLong myHandle
                    PushLong #CacheRecordSize
                    _PtrToHand

                    ldy #owRefCon       ; set the RefCon to the handle
                    lda myHandle
                    sta [temp1],y
                    iny
                    iny
                    lda myHandle+2
                    sta [temp1],y

                    rts

myHandle            ds 4

                    end

                    EJECT
*******************************************************************************
*
CloseApp            start
*
* Description:      Close down things. This disposes of all items and memory
*                   that we allocated.
*
*
* Inputs:           NONE
*
* Outputs:          NONE
*
* External Refs:    NONE
*
* Entry Points:     NONE
*
*******************************************************************************
                    using Globals

; Sly Trick #1!!! Traverse the window list, clearing the visible
; bit in each one. Then, when the window manager closes down, it
; will think that all windows are hidden, and it will NOT erase
; them from the screen. This makes quitting faster!

                    pha                 ; get a pointer to the first
                    pha                 ; window.
                    _GetFirstWindow
                    PullLong Temp1

loop                ANOP
                    pha                 ; get the window's bit flags
                    PushLong Temp1
                    _GetWFrame
                    pla

                    and #$FFFF-fVis     ; turn off the Vis Flag

                    pha                 ; now send it back out
                    PushLong Temp1
                    _SetWFrame

                    pha                 ; get the next window in the
                    pha                 ; list
                    PushLong Temp1
                    _GetNextWindow
                    PullLong Temp1

                    lda Temp1           ; If NULL ($0000 0000) was returned
                    bne loop            ; then we are out to windows to
                    lda Temp1+2         ; fix.
                    bne loop

                    rts
                    end

                    EJECT
*******************************************************************************
*
EventLoop           start
*
* Description:      Main Event Loop. Handle things until user selects Quit.
*
*
* Inputs:           NONE
*
* Outputs:          NONE
*
* External Refs:
*                   Import MenuSelect
*                   Import ignore
*                   Import doUpdate
*                   Import doClose
*
* Entry Points:     NONE
*
*******************************************************************************
                    using Globals

                    pha                 ; Push on space for TaskMaster result
                    PushWord #EveryEvent ; GetNextEvent mask
                    PushLong #EventRecord ; Pointer to Event Record
                    _TaskMaster

                    pla                 ; Get TaskMaster result
                    beq EventLoop       ; Remove if you want to use null events
                    asl A               ; Turn it into an index
                    tax
                    jsr (TaskTable,x)   ; Call appropriate event handler

                    lda QuitFlag        ; Quit selected?
                    beq EventLoop       ; no - keep looping

                    rts                 ; yes- leave the program

TaskTable           dc i2'Ignore'       ; 0 Null
                    dc i2'Ignore'       ; 1 MouseDown
                    dc i2'Ignore'       ; 2 MouseUp
                    dc i2'Ignore'       ; 3 KeyDown
                    dc i2'Ignore'       ; 4 undefined
                    dc i2'Ignore'       ; 5 AutoKey
                    dc i2'Ignore'       ; 6 Update
                    dc i2'Ignore'       ; 7 undefined
                    dc i2'Ignore'       ; 8 Activate
                    dc i2'Ignore'       ; 9 Switch
                    dc i2'Ignore'       ; 10 Desk accessory
                    dc i2'Ignore'       ; 11 Device driver
                    dc i2'Ignore'       ; 12 ap
                    dc i2'Ignore'       ; 13 ap
                    dc i2'Ignore'       ; 14 ap
                    dc i2'Ignore'       ; 15 ap
                    dc i2'Ignore'       ; TASK 0 indesk
                    dc i2'MenuSelect'   ; TASK 1 in menuBar
                    dc i2'Ignore'       ; TASK 2 in system window
                    dc i2'Ignore'       ; TASK 3 in content
                    dc i2'Ignore'       ; TASK 4 in Drag
                    dc i2'Ignore'       ; TASK 5 in grow
                    dc i2'DoClose'      ; TASK 6 in goaway
                    dc i2'Ignore'       ; TASK 7 in zoom
                    dc i2'Ignore'       ; TASK 8 in info bar
                    dc i2'MenuSelect'   ; TASK 9 in special menu
                    dc i2'Ignore'       ; TASK 10 in NDA
                    dc i2'Ignore'       ; TASK 11 in frame
                    dc i2'Ignore'       ; TASK 12 in drop

                    end

                    EJECT
*******************************************************************************
*
MenuSelect          start
*
* Description:      This routine is called when TaskMaster returns a menu
*                   event. It takes the menu item that was hit and calculates
*                   an offset into the menu dispatch table. It then calls that
*                   routine and unhilites the menu when it is done.
*
* Inputs:           TaskData holds menu item selected.
*
* Outputs:          NONE
*
* External Refs:
*                   Import ignore
*                   Import doClose
*                   Import doAbout
*                   Import doQuit
*                   Import doOpenTest
*                   Import doShowOdy
*
* Entry Points:     NONE
*
*******************************************************************************
                    using Globals

                    lda TaskData        ; Get the ID of the menu item selected.
                    sec                 ; Turn it into an index by subtracting
                    sbc #250            ; the starting ID number (25) and mul-
                    asl a               ; tiplying by 2 (each table entry con-
                    tax                 ; sists of 2 bytes).
                    jsr (menuTable,x)   ; Call the routine behind it.

                    PushWord #0         ; Routine done - unhilite the menubar.
                    PushWord taskData+2
                    _HiLiteMenu

                    rts

MenuTable           dc i2'Ignore'       ; undo
                    dc i2'Ignore'       ; cut
                    dc i2'Ignore'       ; copy
                    dc i2'Ignore'       ; paste
                    dc i2'Ignore'       ; clear
                    dc i2'doclose'      ; close
                    dc i2'doAbout'
                    dc i2'doQuit'
                    dc i2'DoOpenTest'   ; open 5 windows with stuff in them
                    dc i2'DoShowOdy'    ; purge the refcons
                    end

                    EJECT
*******************************************************************************
*
Ignore              start
*
* Description:      Called when I want to ignore an event.
*
*
* Inputs:           NONE
*
* Outputs:          NONE
*
* External Refs:    NONE
*
* Entry Points:     NONE
*
*******************************************************************************

                    rts
                    end

                    EJECT
*******************************************************************************
*
DoOpenTest          start
*
* Description:      Open the 5 test windows that we created in InitApp. It
*                   merely loops through an array of 5 window pointers and
*                   calls SelectWindow and ShowWindow for all of them. It then
*                   disables the menu item that was used to call this routine
*                   and enables the Close menu Item.
*
*
* Inputs:           NONE
*
* Outputs:          NONE
*
* External Refs:    NONE
*
* Entry Points:     NONE
*
*******************************************************************************
                    using Globals

                    lda #4              ; we need to count through 5 windows
loop                ANOP
                    pha                 ; save the counter on the stack
                    asl a               ; turn it into an index
                    asl a
                    tax
                    lda wPtr1+2,x       ; get a window pointer from my array
                    tay                 ; of test windows.
                    lda wPtr1,x

                    phy                 ; push on window ptr for _SelectWindow
                    pha
                    phy                 ; push on window ptr for _ShowWindow
                    pha

                    _SelectWindow       ; Bring the window to front
                    _ShowWindow         ; Make it visible

                    pla                 ; get my counter back
                    dec a               ; loop down to the next window.
                    bpl loop            ; if >= 0 keep looping

                    PushWord #258       ; Disable 'Open Test Windows' item
                    _DisableMItem

                    PushWord #255       ; windows are open, enable close item
                    _EnableMItem

                    rts
                    end

                    EJECT
*******************************************************************************
*
DoShowOdy           start
*
* Description:      Show the window that displays the first few pages of John
*                   Buddy's book. This routine Selects the window, and then
*                   Shows it to make it visible. It finishes up by disabling
*                   the Read a Book menu item and enabling the Close item.
*
*
* Inputs:           NONE
*
* Outputs:          NONE
*
* External Refs:    NONE
*
* Entry Points:     NONE
*
*******************************************************************************
                    using Globals

                    PushLong OdyWindow
                    _SelectWindow

                    PushLong OdyWindow
                    _ShowWindow

                    PushWord #255       ; windows are open - enable close item
                    _EnableMItem

                    PushWord #259       ; Disable 'Read Book' item
                    _DisableMItem

                    rts
                    end

                    EJECT
*******************************************************************************
*
DoClose             start
*
* Description:      Closes the top window by hiding it. If it closes the last
*                   window on the screen, the Close menu item is disabled.
*
*
* Inputs:           NONE
*
* Outputs:          NONE
*
* External Refs:    NONE
*
* Entry Points:     NONE
*
*******************************************************************************
                    using Globals

; While closing the window, we have to get a little tricky. If we are closing
; the Odyssey window, then we have to enable the "Read a Book" menu item. If
; we are closing any of the Test window, we have to enable the "Open Test
; Windows" menu item. We perform this check by getting the pointer to the top
; window, and comparing it to the stored pointer to the Odyssey window. If they
; match, then we know we are closing the Odyssey window. If not, then we assume
; that we are closing a Test window. We don't have to worry about NDA windows
; getting in the way as those are taken care of TaskMaster before we ever get
; here.
;

                    pha                 ; get the front window
                    pha
                    _FrontWindow

                    stz WindType        ; Assume we're closing a test window.

                    lda 1,s             ; Check to see if we are closing
                    cmp OdyWindow       ; the Odyssey window
                    bne HideIt
                    lda 3,s
                    cmp OdyWindow+2
                    bne HideIt

                    inc WindType        ; set flag to say we closed Odyssey
HideIt              ANOP
                    _HideWindow         ; hide the top window

                    lda WindType        ; did we close a test window?
                    bne EnableOdy       ; no, enable "Read a Book"

                    PushWord #258       ; yes, so re-enable that menu item
                    _EnableMItem        ; to let us re-open it.
                    bra CheckForMoreWindows

EnableOdy           ANOP
                    PushWord #259       ; re-enable Read A Book menu item
                    _EnableMItem        ; to let us re-open it.

CheckForMoreWindows ANOP
                    pha                 ; any more front windows?
                    pha
                    _FrontWindow

                    plx
                    pla
                    bne done            ; yes, so leave
                    txa
                    bne done            ; yes, so leave

                    PushWord #255       ; no, so disable close item
                    _DisableMItem

done                ANOP
                    rts
WindType            ds 2
                    end

*******************************************************************************
*
CacheContent        start
*
* Description:      This routine draws the contents of the window. Since it
*                   is called from TaskMaster, we need to set the Direct Page
*                   and Data Bank registers. The we prepare to draw the window.
*                   First we check to see if there is a handle in the window's
*                   RefCon, and make sure that it is not purged. If it is
*                   still valid, it is used to update the window. If not, then
*                   the window is drawn by hand.
*
*
* Inputs:           NONE
*
* Outputs:          NONE
*
* External Refs:
*                   Import GetOffScreenBuffer
*                   Import DrawByHand
*
* Entry Points:
*                   Export oldPort      ; used by DrawByHand
*
*******************************************************************************
                    using Globals

;
; CacheContents is called from the Window Manager to update the contents
; of the window. Therefore, the Direct Page and Data Bank Register may
; not be set to what our application had them set to. Save the settings
; as the Window Manager had them, and then set them up the way we like
; them. That way we can use local (2-byte absolute) addressing, and can
; access our Direct Page variables.

                    phd                 ; Save caller's direct page
                    phb                 ; Save caller's data bank register

                    phk                 ; switch data bank to Program Bank
                    plb
                    lda MyDP            ; switch Direct Page to application's
                    tcd

SetUp               ANOP

; Get the port of the window that we have to update

                    pha                 ; space for result of GetWRefCon
                    pha
                    pha                 ; space for result of GetPort
                    pha
                    _GetPort

                    lda 1,s             ; save the current port for later
                    sta oldPort         ; when we leave this routine. the port
                    lda 3,s             ; gets changed if we have to draw into
                    sta oldPort+2       ; the offscreen buffer.

                    _GetWRefCon         ; Get refcon, which holds a handle to
                    PullLong Deref      ;   the cache record.

                    ldy #4              ; Lock the handle to the cache record.
                    lda [deref],y       ; We do this using the method shown
                    ora #attrLocked     ; in the description of HLock in the
                    sta [deref],y       ; memory manager chapter.
                    dey
                    dey
                    lda [deref],y       ; dereference it. Get a pointer to the
                    sta CPPtr+2         ; cache record.
                    lda [deref]
                    sta CPPtr

                    ldy #4              ; See if we have cached the contents.
                    lda [CPPtr],y       ; We determine this by seeing if the
                    iny                 ; pointer is a NULL pointer.
                    iny
                    ora [CPPtr],y       ; see if the pointer is $0000 0000
                    bne haveBuffer      ; no it's not, so update from offscreen

; We don't have an offscreen buffer, so this is what we are going to do. First
; save the old port (the window that we are updating). Then, try to get an
; offscreen GrafPort for buffering. If that is successful, then the port will
; be set to it. Regardless of the outcome, however, we will draw the entire
; contents to whatever is active. Then we check the status of the
; GetOffScreenBuffer call. If it succeeded, then copy the buffer's contents
; to the window. If it didn't then what we drew appeared on the screen anyway,
; and we're done.


                    lda oldPort         ; pass the window pointer in X & Y
                    ldx oldPort+2
                    jsr GetOffScreenBuffer ; changes the port if successful
                    php                 ; save the status of this call
                    jsl DrawByHand      ; draw the contents to cache or screen

                    PushLong oldPort    ; reset the port to window port
                    _SetPort

                    plp                 ; Did offscreen caching occur?
                    bcc SetUp           ; yes - now try again
                    bcs done            ; no - we drew to screen - so exit.

haveBuffer          ANOP
;
; We have an offscreen buffer. Prepare the stack for a PPToPort call.
;
                    ldy #6              ; push on source LocInfo pointer
                    PushWord [CPPtr],y  ; This pointer points to the LocInfo
                    dey                 ; of the OffScreen buffer, and is
                    dey                 ; stored in the Cached Record.
                    PushWord [CPPtr],y

;
; We push on a pointer to VisRect here, but haven't yet initialized it! But
; don't worry. We will fill in VisRect down below.
;
                    PushLong #VisRect   ; pass the pointer to the visrect.

                    lda oldPort         ; get the pointer to the dest port.
                    sta deref
                    lda oldPort+2
                    sta deref+2

; Now we need to tell PPToPort what part of the screen to update. This is
; done by looking at the VisRgn (which contains the intersection of the
; update region and the VisRgn). The format of a region is:
;
;  WORD: rgnSize  Size of the region
;  RECT: rgnBBox  rectangle the just surrounds the region
;  XXXX: rgnData  region data the would get me fired if I told you what it was.
;
; By using rgnBBox as our source/destination rectangle, we make PPToPort do
; as little work as possible; There won't be anything to update outside of
; that rectangle.
;
                    ldy #oVisRgn+2      ; Find the visible rectangle.
                    lda [deref],y       ; First get the handle to the visRgn.
                    sta Temp2+2
                    dey
                    dey
                    lda [deref],y
                    sta Temp2

                    ldy #2              ; deref the VisRgn handle
                    lda [Temp2],y
                    sta Temp1+2
                    lda [Temp2]
                    sta Temp1

                    ldy #8              ; finally copy the rect out of the
loop                lda [Temp1],y       ; VisRgn into a local variable.
                    sta VisRect-2,y
                    dey
                    dey
                    bne loop

;
; VisRect now holds the rectangle that surrounds the entire area to udpate.
; This makes that "PushLong #VisRect" above point to valid data!
;
; Now we push on the destination for the PPToPort command. Since PPToPort
; doesn't do any scaling, all we need is the upper-left hand point of the
; destination; the lower left corner is calculated by QuickDraw by using the
; height and width of the source.

                    PushLong VisRect    ; use the upper left hand corner of the
;                                       ; source rectangle as the destination.

                    PushWord #modeCopy  ; Set the transfer mode.
                    _PPToPort           ; Blit those bits!!!

                    pha                 ; push on room for GetWRefcon result
                    pha
                    PushLong oldPort
                    _GetWRefCon
                    _HUnlock            ; unlock the Cache record handle

done                ANOP
                    plb                 ; restore caller's data bank register
                    pld                 ; restore caller's direct page reg.

                    rtl                 ; return to TaskMaster

oldPort             entry
                    ds 4
VisRect             ds 8

                    end

                    EJECT
*******************************************************************************
*
SlowDraw            start
*
* Description:      This routine calls the routine that draws the test window
*                   contents. It takes the place of the 'CacheContents'
*                   routine for Windows 1 and 2. It does NOT cache the window
*                   contents, and is used to show the difference between
*                   cached and non-cached updating.
*
*
* Inputs:
*
* Outputs:
*
* External Refs:
*                   Import DrawContent
*
* Entry Points:
*
*******************************************************************************
                    using Globals

;
; SlowDraw is called from the Window Manager to update the contents
; of the window. Therefore, the Direct Page and Data Bank Register may
; not be set to what our application had them set to. Save the settings
; as the Window Manager had them, and then set them up the way we like
; them. That way we can use local (2-byte absolute) addressing, and can
; access our Direct Page variables.
;
                    phd
                    phb

                    phk
                    plb
                    lda MyDP
                    tcd

                    pha
                    pha
                    _GetPort
                    pla
                    plx

                    jsl DrawContent

                    plb
                    pld

                    rtl
                    end

                    EJECT
*******************************************************************************
*
DrawByHand          start
*
* Description:      The contents need to be created for the first time. The
*                   address of theroutine that doesthis is in a parameter
*                   block pointed to by the RefCon. Get this address, push it
*                   on the stack, and load up A and X with the pointer to the
*                   window so that the drawing routine can have information
*                   about it (the actual port it is drawing to is an offscreen
*                   port, and does not have window information in it, like the
*                   window title).
*
*
* Inputs:           NONE
*
* Outputs:          NONE
*
* External Refs:
*                   Import oldPort
*
* Entry Points:     NONE
*
*******************************************************************************
                    using Globals

                    ldy #1              ; Get bytes 1 and 2 of drawing routine.
                    PushWord [CPPtr],y  ; push bytes 1 and 2 on.
                    phb                 ; push one byte onto the stack.
                    lda [CPPtr]         ; Get bytes 0 and 1 into Acc.
                    sta 1,s             ; now store bytes 0 and 1 on the stack
                    lda oldPort         ; set up A & X for the drawing routine
                    ldx oldPort+2
                    rtl                 ; RTL to the drawing routine

                    end

                    EJECT
*******************************************************************************
*
DrawOdyRtn          start
*
* Description:      This routine posts the first few pages of Sculley's book
*                   into the Cache buffer or onto the screen. It uses TextBox2
*                   and takes a while, so tell the user by showing a watch.
*
*                   None of what I do here requires the use of my Direct Page
*                   or 2-byte absolute addressing, so I don't need to change
*                   the Direct Page or Data Bank Registers. However, other
*                   routines that perform updating will normally need to change
*                   these. The CacheContents routine demonstrates the
*                   technique.
*
*
* Inputs:           A = low order word of window pointer
*                   X = high order word
*
* Outputs:          NONE
*
* External Refs:    NONE
*
* Entry Points:     NONE
*
*******************************************************************************
                    using Globals

;
; --- We'll be sitting here for a while, so tell the user.
;
                    _WaitCursor

                    PushLong #Odyssey   ; push pointer to text
                    PushWord #OdysseyEnd-Odyssey            ; length of text
                    PushLong #OdyRect   ; bounds to fit the text into
                    PushWord #0         ; Fill justify
                    _LETextBox2         ; draw it all!

                    _InitCursor         ; set cursor back to arrow.

                    rtl                 ; back to caller.

OdyRect             dc i2'0,10,790,590'

                    end

                    EJECT
*******************************************************************************
*
DrawContent         start
*
* Description:      This is the real procedure that draws the contents of the
*                   Test windows. It merely draws the name of the window many
*                   times.
*
*
* Inputs:           A = low order word of window pointer
*                   X = high order word
*
* Outputs:          NONE
*
* External Refs:    NONE
*
* Entry Points:     NONE
*
*******************************************************************************
                    using Globals

                    sta myPort          ; save window pointer
                    stx myPort+2

                    pha                 ; room for window title pointer
                    pha
                    phx                 ; push on window pointer
                    pha
                    _GetWTitle          ; get the window's title
                    PullLong wTitle

                    lda #10             ; now initialize the X and Y variables
                    sta yLoc            ; that we use to draw the strings.
                    lda #0
                    sta xLoc
                    lda #10             ; loop 10 times down the screen
                    sta bigCounter
loop                ANOP
                    PushWord xLoc       ; set up for the Moveto
                    PushWord yLoc
                    _MoveTo

                    lda #8              ; loop 8 times across the screen
                    sta littleCounter
loop2               ANOP
                    PushLong wTitle     ; put window title pointer onto stack
                    _DrawString         ; and draw the string
                    dec littleCounter   ; update our horizontal counter
                    bpl loop2

                    lda yLoc            ; update the X/Y coordinates
                    clc
                    adc #10
                    sta yLoc
                    lda xLoc
                    sec
                    sbc #20
                    sta xLoc
                    dec BigCounter      ; update our vertical counter
                    bpl loop            ; ...and check it.

                    rtl

xLoc                ds 2                ; where to start drawing string next
yLoc                ds 2                ; where to start drawing string next
littleCounter       ds 2                ; counter across the screen
bigcounter          ds 2                ; counter down the screen

myPort              ds 4
wTitle              ds 4

                    end

                    EJECT
*******************************************************************************
*
GetOffScreenBuffer  start
*
* Description:      Get a handle to an offscreen GrafPort. If _NewHandle call
*                   fails, set the carry and return. If it succeeds, create a
*                   new bitmap for it, install the bitmap, and initialize the
*                   appropriate fields. Finally, install the pointer to the
*                   GrafPort in the cache record for the window, clear the
*                   carry, and return with the port set to the new port.
*
*                   This routine is entered with the pointer to the window
*                   manager port of the window we are updating. In case we
*                   cannot get an offscreen buffer, we can reset the port to
*                   this.
*
*
* Inputs:           A = low word of window pointer of window we are updating
*                   X = high word
*
* Outputs:          NONE
*
* External Refs:    NONE
*
* Entry Points:     NONE
*
*******************************************************************************
                    using Globals

                    sta origPort        ; save the window manager port
                    stx origPort+2

;
; --- Get a memory for a new grafport
;
                    stz newPort         ; assume that we don't get it.
                    stz newPort+2
                    pha                 ; space for the new handle
                    pha
                    PushLong #portSize
                    PushWord MyID
                    PushWord #attrFixed ; plus 'attrNoCross' if on old ROMs
                    PushLong #$0000
                    _NewHandle
                    PullLong deref
                    bcs error

                    ldy #2              ; Dereference and lose the handle
                    lda [deref],y       ; (we only need a pointer)
                    tax
                    lda [deref]
                    sta newPort
                    stx newPort+2

                    phx                 ; Now initialize it as a GrafPort
                    pha
                    _OpenPort
                    bcs error
;
; --- We now have a port to play with, but its PixImage is the
; --- screen. Let's change that.
;
                    jsr AddPixMap
                    bcs error

; Everything is all set up. The current port should still be the one
; we just created with the _OpenPort above. All we have to do is install
; the pointer to it into the cacheParms block for the Window.

                    ldy #4
                    lda newPort
                    sta [CPPtr],y
                    iny
                    iny
                    lda newPort+2
                    sta [CPPtr],y

                    clc
                    rts
error               ANOP
                    pha                 ; Error occured. Get the handle to the
                    pha                 ; port that we created; dispose of it.
                    PushLong newPort
                    _FindHandle
                    bcs skip
                    _DisposeHandle
skip                ANOP
                    PushLong origPort
                    _SetPort
                    sec
                    rts

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; This routine adds an offscreen PixMap to the Grafport pointed to by
; 'newPort'.
;
AddPixMap           ANOP
                    jsr CalcScrnDims    ; get values for LocInfo

                    pha                 ; get a PixImagePtr of right size
                    pha
                    PushLong scrnSize
                    PushWord MyID
                    PushWord #attrFixed ; plus 'attrNoCross' if on old ROMs
                    PushLong #$0000
                    _NewHandle
                    PullLong Deref
                    bcs error2

;
; Get the Window Pointer into a Direct Page location (Temp1) for
; easy access later.
;
                    lda newPort
                    sta Temp1
                    lda newPort+2
                    sta Temp1+2

                    ldy #2              ; Dereference and lose the handle
                    lda [deref],y       ; to the PixImage (it's locked)
                    tax
                    lda [deref]         ; (pointer in A and X)

                    ldy #optrToPixImage ; install the new PixImage
                    sta [Temp1],y
                    iny
                    iny
                    txa
                    sta [Temp1],y

                    ldy #owidth         ; set the 'rowBytes' value
                    lda rowBytes
                    sta [Temp1],y

                    ldy #oboundsRect+4  ; update the size of the boundsRect
                    lda scrnHeight
                    sta [Temp1],y
                    iny
                    iny
                    lda scrnWidth
                    sta [Temp1],y

                    ldy #oPortRect+4    ; and the size of the portrect
                    lda scrnHeight
                    sta [Temp1],y
                    iny
                    iny
                    lda scrnWidth
                    sta [Temp1],y

;
; Set the VisRgn and ClipRgn to the size of the port we just
; created. This is done by calling ClipRect with the portRect,
; and then copying the resulting ClipRgn to the VisRgn.
;
                    lda Temp1           ; Create a pointer to the portRect
                    clc                 ; in the Window Record (remember,
                    adc #oPortRect      ; Temp1 points to the WindRec).
                    tax
                    lda Temp1+2
                    adc #0
                    pha                 ; push on one set for _EraseRect
                    phx
                    pha                 ; push on this set for _ClipRect
                    phx
                    _ClipRect

                    ldy #oClipRgn+2     ; now copy the ClipRgn to the VisRgn
                    PushWord [Temp1],y
                    dey
                    dey
                    PushWord [Temp1],y
                    _SetVisRgn

                    _EraseRect          ; rect ptr left on from way before.

                    clc
                    rts

Error2              ANOP
                    sec
                    rts

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Calculate the screen size based on the general formula:
;
;    rowbytes := (((((right - left) * theDepth) + 7) DIV 8) + 7) AND ~%111
;
; in English, or what passes for it when I type, this says: take the width
; of the GrafPort, and multiply it by the depth (number of bits per pixel)
; of the screen. Add 7 to this and divide by 8 to get the number of bytes
; that this takes up (add 7 effectively rounds up, to counter the DIV 8's
; rounding down). After we have the number of bytes, we have to make sure that
; this is an "even multiple of 8", as it states in the QuickDraw Chapter. This
; is done by adding 7 (again, to round up) and stripping off the low 3 bits,
; which performs the rounding down to the nearest multiple of 8.
;
; This formula can be slightly reduced to:
;
;    rowbytes := ((((right - left) * theDepth) + 63) DIV 8) AND ~%111
;
; which is used in specific:
;
;    rowBytes := (((scrnWidth * 2) + 63) DIV 8) AND ~%111
;
CalcScrnDims        ANOP
                    ldy #10             ; get the screen width from cacheParms
                    lda [CPPtr],y
                    sta scrnWidth
                    asl a               ; times 2
;                   asl a               ; use this also if in 320 mode
                    clc                 ; add 63
                    adc #63
                    lsr a               ; divide by 8
                    lsr a
                    lsr a
                    and #%11111000      ; and with ~%111
                    sta rowbytes
                    pha                 ; Now prepare to multiply to get
                    pha                 ; the whole screen size.
                    pha                 ; Multiplier
                    dey
                    dey
                    lda [CPPtr],y
                    sta scrnHeight
                    pha                 ; Multiplicand
                    _Multiply
                    PullLong ScrnSize
                    rts

origPort            ds 4
newPort             ds 4

scrnSize            ds 4
scrnHeight          ds 2
scrnWidth           ds 2
rowBytes            ds 2

                    end

                    copy Jiffy.stds.src

                    END
